CAGE Analysis with CAGEr

Loading in data from an R-package

Here, we will do a sample workflow for two samples at different stages in zebrafish development: 512 cell stage (~2,7 hours hpf) and Prim 6 (~24 hpf). These stages have been previously published by Nepal et al (2013) and are available in an R package (by Haberle). Similar packages are available for mice and human data.
For the first part of the workflow we will use the R package called “CAGEr”. We will do some of the standard workflow and gradually manipulate the data ourselves in R.
So let’s start by loading in the package for the developmental stages as well as CAGEr:

# packages
require(ZebrafishDevelopmentalCAGE)
require(CAGEr)

Loading the samples of which we have data and display which stages we have here:

# loading data
data(ZebrafishSamples)
head(ZebrafishSamples) 
# samples
samples <- as.character(ZebrafishSamples$sample) # to see all the samples
samples
 [1] "zf_unfertilized_egg" "zf_fertilized_egg"   "zf_64cells"         
 [4] "zf_512cells"         "zf_high"             "zf_oblong"          
 [7] "zf_sphere_dome"      "zf_30perc_dome"      "zf_shield"          
[10] "zf_14somites"        "zf_prim6"            "zf_prim20"          

As said earlier, we will pick just two developmental stages: “zf_512cells” and “zf_prim6”. In the vignette (and manual) the way of loading in these data is by the function importPublicData(). We need to specify the source, dataset, group, and samples. The source is “ZebrafishDevelopment” and the rest are found in the dataframe of the ZebrafishSamples: ZebrafishCAGE, development, samplenames. The avoid spelling everything out we’ll use the vector of samples:

# creating a CAGEset object:
myCAGEset <- importPublicData(source = "ZebrafishDevelopment", dataset = "ZebrafishCAGE", 
group = "development", sample = samples[c(4,11)]  )
# what does it look like?
myCAGEset

S4 Object of class CAGEset

=======================================
Input data information
=======================================
Reference genome (organism): BSgenome.Drerio.UCSC.danRer7
Input file type: ZebrafishDevelopment
Input file names: ZebrafishDevelopment__zf_512cells, ZebrafishDevelopment__zf_prim6
Sample labels: zf_512cells, zf_prim6
=======================================
CTSS information
=======================================
CTSS chromosome: chr1, chr1, chr1, ...
CTSS position: 3783, 3788, 3798, ...
CTSS strand: +, +, +, ...
Tag count:
    -> zf_512cells: 0, 0, 0, ...
    -> zf_prim6: 1, 1, 1, ...
Normalized tpm:
=======================================
Tag cluster (TC) information
=======================================
CTSS clustering method: 
Number of TCs per sample:
=======================================
Consensus cluster information
=======================================
Number of consensus clusters:
Consensus cluster chromosome:
Consensus cluster start:
Consensus cluster end:
Consensus cluster strand:
Normalized tpm:
=======================================
Expression profiling
=======================================
Expression clustering method: 
Expression clusters for consensus clusters:
=======================================
Promoter shifting
=======================================
GroupX:
GroupY:
Shifting scores:
KS p-values (FDR adjusted):

Here, it is good to double check the reference genome and the sample labels. Also this gives us all the slots of the S4 object. So at this stage we have CTSS information as this is already the form of the data of the package. Only the tag count for each TSS for both samples, as the slot of Normalized tpm is empty. As well as the rest of the object.

Functions of CAGEr

With the following few standard CAGEr functions we will “fill in” these slots. And when full, show you how we can retrieve and export of these data.

Correlation plots

First, we will look at the data globally between the samples. CAGEr has a function do this for you (shown below) to look at the correlation between the samples and plots the png in the working directory. We fill first source an adjusted version of this function to control the output by using source (Creating a new generic function for ‘plotCorrelation’).

corr.m <- plotCorrelation(myCAGEset, samples = "all", method = "pearson") 
corr.m # correlation
            zf_512cells  zf_prim6
zf_512cells   1.0000000 0.4510721
zf_prim6      0.4510721 1.0000000
correlation

correlation

Normalization

Next, we need to normalize our data to adjust for example different library sizes to make them comparable. Here, we’ll use the power-law based normalization (see @balwierz).

librarySizes(myCAGEset)
[1] 4155747 7668701
# normalisation first plot to assess alpha
plotReverseCumulatives(myCAGEset, fitInRange = c(5, 1000), onePlot = TRUE) # plot
# insert alpha in normalize function:
normalizeTagCount(myCAGEset, method = "powerLaw",fitInRange = c(5, 1000), alpha = 1.20, T = 1*10^6)

Now the CTSS information the normalized tpm is added:

myCAGEset

Tag clusters of CTSSs

Let’s create tag clusters of CTSSs with the clusterCTSS function of CAGEr. CTSS in close proximity of each other give rise to functionally simialr set of transcripts within the same promoter elements. Tag clusters (TCs) are basically larger transcriptional units that correspond to individual promoters.
Here, we will use a simple distance measurement that individual CTSS can not be more than 20 bp apart. We also set the threshold at 1 which means that each TSS should have 2 or more tag counts in all the samples prior to clustering. Additionally, to remove low fidelity TSSs: no TC are called with a single TC if the normalized signal is below 5.

# Clustering of CTSS :)
clusterCTSS(object = myCAGEset, threshold = 1, thresholdIsTpm = TRUE, 
            nrPassThreshold = 1, method = "distclu", maxDist = 20, 
            removeSingletons = TRUE, keepSingletonsAbove = 5)

Keep in mind that these are determined per sample

Have a look again at the myCAGEset! The Tag cluster information slot is filled in.

Promoter width

Another feature we can check is the promoter width. That is, the width of each tag cluster per sample. CAGEr has a function that calculates the TC width by caculating cumulative distribution of tag signal per TC. Subsequently, promoter (TC) width is defined as the distance (in bp) between two quantiles generally set at 0.1 and 0.9 to capture 80% of tags. cumulative promoter

# calculate cumlative CTSS distribution:
cumulativeCTSSdistribution(myCAGEset, clusters = "tagClusters")
# determine quantile positions:
quantilePositions(myCAGEset, clusters = "tagClusters", qLow = 0.1, qUp = 0.9)

We can then plot a histogram of the interquantile widths per sample with CAGEr too.

# we can plot the interquantile width with CAGEr too
plotInterquantileWidth(myCAGEset, clusters = "tagClusters",tpmThreshold = 3, qLow = 0.1, qUp = 0.9)

If you look at these two plots, you can see that the 512 cell stage more narrower TCs than prim6 which is what we would expect.

Consensus Clusters

CAGEr vignette:
Tag clusters are often sample-specific, thus can be present in one sample but absent in another. In addition, in many cases tag clusters do not coincide perfectly within the same promoter region, or there might be two clusters in one sample and only one larger in the other. To be able to compare genome-wide transcriptional activity across samples and to perform expression profiling, a single set of consensus clusters needs to be created. This is done using aggregate-TagClusters function, which aggregates tag clusters from all samples into a single set of non-overlapping consensus clusters:

aggregateTagClusters(myCAGEset, tpmThreshold = 5, qLow = 0.1, qUp = 0.9, maxDist = 100)

Expression - Self organising maps

As CAGE signals is essentially transcription, levels can be compared between groups. This can be done per CTSS, TC, or consensus clusters. CAGEr offers two methods to cluster gene expression, k-means and self-organising maps (SOM). Both require the number of expression cluster to be known in a priori.
Here we will perform the SOM algorithm at consensus cluster level. We set the threshold to at least 10 tpm (normalized) in at least one sample.

getExpressionProfiles(myCAGEset, what = "consensusClusters", tpmThreshold = 10,
        nrPassThreshold = 1, method = "som", xDim = 3, yDim = 2)
# in a nice plot:
plotExpressionProfiles(myCAGEset, what = "consensusClusters")

Promoter shifting

Promoter shifting is a method described by Haberle et al in 2014. They’ve shown that the same promoter can be used differently in different samples. This method has been implemented in CAGEr. Shifting can be detected between two individual samples (or between two groups of samples that are merged per group)

For all promoters a shifting score is calculated based on the difference in the cumulative distribution of CAGE signal along that promoter in the two samples. In addition, a more general assessment of differential TSS usage is obtained by performing Kolmogorov-Smirnov test on the cumulative distributions of CAGE signal, as described below. Thus, prior to shifting score calculation and statistical testing, we have to calculate cumulative distribution along all consensus clusters:

cumulativeCTSSdistribution(myCAGEset, clusters = "consensusClusters")

Calculating cumulative sum of CAGE signal along clusters...
    -> zf_512cells
    -> zf_prim6

Determine the shifting score:

Values of shifting score are in range between -Inf and 1. Positive values can be interpreted as the proportion of transcription initiation in the sample with lower expression that is happening “outside” (either upstream or downstream) of the region used for transcription initiation in the other sample. In contrast, negative values indicate no physical separation, i.e. the region used for transcription initiation in the sample with lower expression is completely contained within the region used for transcription initiation in the other sample.

To assess any general change in the TSS usage within the promoter region, a twosample Kolmogorov-Smirnov (K-S) test on cumulative sums of CAGE signal along the consensus cluster is performed. K-S test is performed to assess whether the two underlying probability distributions differ. To obtain a P-value, sample sizes that generated the ECDFs are required, in addition to actual K-S statistics calculated from ECDFs. These are derived either from raw tag counts (when useTpmKS = FALSE), or from normalized tpm values (when useTpmKS = TRUE). P-values obtained from K-S tests are further corrected for multiple testing using Benjamini and Hochenberg (BH) method and for each P-value a corresponding false-discovery rate (FDR) is also reported.

Here we will set the threshold of shifting score at 0.6 like the original paper as well as setting the FDR at 0.01:

This returns genomic coordinates (of conensusclusters), shifting score, and P-value (FDR) of the promoters, as well as the value of CAGE signal and position of the dominant TSS in the two compared (groups of) samples.

colnames(shifting.promoters)[7:8] <- paste(samples[c(4,11)], ".pos", sep = "") # to remember later which was group x and group y
write.table(shifting.promoters, "../Data/intermediate/ShiftedPromoters_sc06_fdr001_2samples.txt", col.names = TRUE,
            row.names = FALSE, quote = FALSE, sep = "\t")

Save myCAGEset as an intermediate file for if you want to revisit it later on

save(myCAGEset, file ="../Data/intermediate/CAGEobject_twoSamples_PowNom_allSlots.RData")

Export browser tracks from CAGEr

Export tables

Data handlin in R

Annotation of transcripts

Where do the cage signal

Dinucleotide plots

Differential Gene Expression

CAGE data can also be used to assess expression of the cTSSs. Here, we will use the R-package DESeq2 [@love_moderated_2014]. Originated for RNA-seq data but can also handle similar data from other assaya types (such as CAGE data). The vignette and reference manual can be displayed by running the code below or can be found here DESeq2.

browseVignettes("DESeq2")

Summary and goals of this practical


CAGEr

  • Prepare the right data format from CAGE data for DESEQ2
  • Export the data from CAGEr

DESeq2

  • Normalise the data
  • Differential expression

Follow up

  • Gene annotation
  • Gene ontology

1 Exporting data from a CAGEset object

Creating a count table input for DESeq2


We have worked until now with the two samples. However, for a differential expression analysis you will need more samples (replicates and/or more of the same condition). To this end, we’ll include two more samples to the mix to follow the more standard work-flow and generate p-values. The two additional samples are again from the same R package (ZebrafishDevelopmentalCAGE). The analysis will be on early stage expression vs later stage expression: zf_64cells and zf_512cells vs zf_prim6 and zf_prim20.

DESeq2 accepts matrices of read counts as input and this is exactly what we will export from CAGEr. It expects count data in the form of a matrix of integer values. The value in the i-th row and the j-th column of the matrix tells how many reads can be assigned to each cTSS site i in sample j. The values in the matrix should be un-normalized counts of sequencing reads.

Consensus site (row) Sample 1 tpm Sample 2 tpm Sample 3 tpm Sample 4 tpm
1 0 1 44 60
2 4 10 6 9

!! “The DESeq2 model internally corrects for library size, so transformed or normalized values such as counts scaled by library size should not be used as input”

The code below was run to produce the consensus clusters for the four samples as this would take to long to do on the day. The one important (different step) is shown below in the code. Don’t run the code (today).

# packages
require(ZebrafishDevelopmentalCAGE)
require(CAGEr)
# load data
data(ZebrafishSamples)
as.character(ZebrafishSamples$sample)
myCAGEset <- importPublicData(source = "ZebrafishDevelopment", dataset = "ZebrafishCAGE", 
group = "development", sample = as.character(ZebrafishSamples$sample[c(3:4,11:12)] ) )

# CTSS tag count
ctss <- CTSStagCount(myCAGEset)

#
# To keep using the raw counts in all downstream steps, the normalizeTagCount function of CAGEr should be used with the method set to "none". Note that normalizeTagCount function has to be applied to CAGEset object before moving to next steps.
normalizeTagCount(myCAGEset, method = "none")
#


# Clustering of CTSS: low fidelity cTSSs are removed (each cluster with only one cTSS signal < 5).
clusterCTSS(object = myCAGEset, threshold = 1, thresholdIsTpm = TRUE, 
            nrPassThreshold = 1, method = "distclu", maxDist = 20, 
            removeSingletons = TRUE, keepSingletonsAbove = 5)

# cumulative distribution and quantile positions
cumulativeCTSSdistribution(myCAGEset, clusters = "tagClusters")
quantilePositions(myCAGEset, clusters = "tagClusters", qLow = 0.1, qUp = 0.9)

# aggregate the clusters across the samples:
aggregateTagClusters(myCAGEset, tpmThreshold = 5, qLow = 0.1, qUp = 0.9, maxDist = 100)
save(myCAGEset, file = "../Data/provided/AggregatedTagClus_0109_4Samples.RData")


To be able to compare transcriptional activity across these samples, consensus clusters will be used for downstream analysis. This is provided to you in a .RData file. The following steps involve creating the count data table for each consensus cluster, merge it with the coordinates of the consensus, and to write a file with the output in the intermediate directory within the Data directory.

# package
require(CAGEr)

# load the data produced by the code above
load("../Data/provided/AggregatedTagClus_0109_4Samples.RData")

# create count tpm matrix per consensus cluster for each sample
count.df <- data.frame(consensusClustersTpm(myCAGEset))

# and the consensus coordinates (same order)
consensus.info <- consensusClusters(myCAGEset)

# create identifiers to link back
consensus.info$cons_clus_id <- paste("cid_",1:nrow(consensus.info), sep = "")
rownames(count.df) <- consensus.info$cons_clus_id

# save the combined info and count tpm as intermediate files 
# the order is the same so we can easily use cbind
count.consensus.info <- cbind(consensus.info[,-1], count.df)
write.table(count.consensus.info, "../Data/intermediate/CountTable_Consensus_4Samples.txt", col.names = TRUE, row.names = FALSE, sep = "\t", quote = FALSE)

# remove unnecessary files 
rm(myCAGEset, samples, consensus.info)


2 DESeq2 Data Analysis


First, we will make a DESeqDataSet object from the count table and add the “formula” which is the design of the analysis downstream (a linear model: ~ condition). In this example we are only using the variable (early vs late), however, if you want to add covariates in the model (e.g. batch) these would be extra columns in info.df (see below) and given in the model like : ~ column name covariate + variable. So the variable will be the last in the model.

# DESeq2 expects a matrix of count table:
count.matrix <- as.matrix(count.df) # if from previously saved file: as.matrix(count.consensus.info[,7:10])
head(count.matrix)
# the condition for the analysis in data.frame:
samples <- colnames(count.matrix)
info.df <- data.frame(condition = factor(x = c("early", "early", "late", "late"), levels = c("early","late")), row.names = samples)


The reason for specifying the levels of the factor is because by default, R will handle factor levels based on alphabetical order. It is good practice to get into, especially in analyses where it matters which level you want to compare against. Thus identifying correctly up- or down-regulated genes in this case.
The first thing to do now is to create a DESeqDataSet object that stores the count matrix and design:

require(DESeq2)
dds <- DESeqDataSetFromMatrix(countData = count.matrix, colData = info.df, design = ~ condition)


Let’s first do a simple heatmap to check how similar (or dissimilar) the four samples are on a genome-wide transcription level. For this we will use the transformed data as performed by DESeq2. It can do three different types of transformation and here we will use the function rlog, which stands for regularized log. It transforms the original count data to the log2 scale by fitting a model with a term for each sample and a prior distribution on the coefficients which is estimated from the data (See DESseq vignette for more info).

# package
require(RColorBrewer)
require(pheatmap)
# Extracting transformed values:
rld <- rlog(dds, blind=FALSE)
# heatmap
sampleDists <- dist(t(assay(rld)))
sampleDistMatrix <- as.matrix(sampleDists)

rownames(sampleDistMatrix) <- rld$condition
colnames(sampleDistMatrix) <- NULL

colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)

pheatmap(sampleDistMatrix, clustering_distance_rows=sampleDists,
        clustering_distance_cols=sampleDists,col=colors)

Indeed, the samples are clustering according to the developmental time frame. There are more things you can do and check such as principal component analysis that are also described in the vignette.

The standard differential expression analysis steps are wrapped into a single function, ` DESeq. Results tables are generated using the function results, which extracts a results table with log2 fold changes, pvalues and adjusted pvalues. The text, condition treated vs untreated, tells you that the estimates are of the logarithmic fold change log2 (treated/untreated).

# the analysis
dds <- DESeq(dds)
# results
res <- results(dds)


Next, we want to know what are the lowest adjusted pvalues as these are of interest, which genes they represent, and gene ontology for patterns in the data.
First, we are adding the genomic coordinates of the cTSS still stored in:

count.consensus.info

and then reorder the results according to lowest p-value:

res.info <- cbind(count.consensus.info[,c(1:4,6)], data.frame(res@listData))
res.info <- res.info[order(res.info$padj),]
head(res.info)

An easy summary:

summary(res)  # orignal S4 output of DESeq

The amount cTSS differentially expressed (padj < 0.05):

sum(res$padj < 0.05, na.rm=TRUE)

Save the results in a table in the Intermediate directory:

# Dataframe for downstream
# save as intermediate file
write.table(res.info, "../Data/intermediate/DiffExpression_Consensus_4Samples.txt", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t")
# remove previous files
rm(res)


3 Gene annotation and Gene ontology


Let’s start by having a txdb agaub for our samples. The txdb was created with the code below and can be found in the provided data directory.

# dan rerio v7
require(GenomicFeatures)
require(AnnotationDbi)
txdb <- makeTxDbFromUCSC("danRer7", "ensGene")
saveDb(txdb, file = "../Data/provided/txdb_DanRer7.sqlite")

Load in the the txdb:

require(GenomicFeatures)
require(AnnotationDbi)
txdb <- loadDb("../Data/provided/txdb_DanRer7.sqlite")

Let’s run through a few things first and build a function for later purposes. First defining gene features to later assess in our differentially expressed cTSSs: promoters, upstream seq (5kb of promoter), exons, introns, and gene as annotated in ensemble. These will be in GRanges objects.

# Promoters (500bp window around refgene TSS)
promoters = trim(promoters(txdb, upstream=500, downstream=500))
# 5 kb upstream - 500bp 
upstream = trim(flank(promoters, 5000))
# exons
exons <- exons(txdb) 
# exons grouped by gene:
exons_gene <- reduce(exonsBy(txdb,"gene"))

# introns
introns = intronsByTranscript(txdb)
# genes
gene = genes(txdb)

To use the useful features of GenomicRanges we will have to convert our differential expression result to a GRanges object too:

# packages
require(BSgenome.Drerio.UCSC.danRer7)

total = res.info 
gr <- GRanges(seqnames = total$chr,
                ranges = IRanges(start = total$start,end = total$end),
                strand = total$strand,
                cons_clus_id = total$clus.gene, # the identifier
                seqlengths = seqlengths(Drerio))

References

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIGludG8gQ0FHRSBkYXRhIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGRmX3ByaW50OiBwYWdlZAotLS0KCiMgQ0FHRSBBbmFseXNpcyB3aXRoIENBR0VyCgojIyBMb2FkaW5nIGluIGRhdGEgZnJvbSBhbiBSLXBhY2thZ2UKSGVyZSwgd2Ugd2lsbCBkbyBhIHNhbXBsZSB3b3JrZmxvdyBmb3IgdHdvIHNhbXBsZXMgYXQgZGlmZmVyZW50IHN0YWdlcyBpbiB6ZWJyYWZpc2ggZGV2ZWxvcG1lbnQ6IDUxMiBjZWxsIHN0YWdlICh+Miw3IGhvdXJzIGhwZikgYW5kIFByaW0gNiAofjI0IGhwZikuIFRoZXNlIHN0YWdlcyBoYXZlIGJlZW4gcHJldmlvdXNseSBwdWJsaXNoZWQgYnkgTmVwYWwgZXQgYWwgKDIwMTMpIGFuZCBhcmUgYXZhaWxhYmxlIGluIGFuIFIgcGFja2FnZSAoYnkgSGFiZXJsZSkuIFNpbWlsYXIgcGFja2FnZXMgYXJlIGF2YWlsYWJsZSBmb3IgbWljZSBhbmQgaHVtYW4gZGF0YS4KPGJyPgpGb3IgdGhlIGZpcnN0IHBhcnQgb2YgdGhlIHdvcmtmbG93IHdlIHdpbGwgdXNlIHRoZSBSIHBhY2thZ2UgY2FsbGVkICJDQUdFciIuIFdlIHdpbGwgZG8gc29tZSBvZiB0aGUgc3RhbmRhcmQgd29ya2Zsb3cgYW5kIGdyYWR1YWxseSBtYW5pcHVsYXRlIHRoZSBkYXRhIG91cnNlbHZlcyBpbiBSLgo8YnI+ClNvIGxldCdzIHN0YXJ0IGJ5IGxvYWRpbmcgaW4gdGhlIHBhY2thZ2UgZm9yIHRoZSBkZXZlbG9wbWVudGFsIHN0YWdlcyBhcyB3ZWxsIGFzIENBR0VyOgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgcGFja2FnZXMKcmVxdWlyZShaZWJyYWZpc2hEZXZlbG9wbWVudGFsQ0FHRSkKcmVxdWlyZShDQUdFcikKYGBgCkxvYWRpbmcgdGhlIHNhbXBsZXMgb2Ygd2hpY2ggd2UgaGF2ZSBkYXRhIGFuZCBkaXNwbGF5IHdoaWNoIHN0YWdlcyB3ZSBoYXZlIGhlcmU6CmBgYHtyfQojIGxvYWRpbmcgZGF0YQpkYXRhKFplYnJhZmlzaFNhbXBsZXMpCmhlYWQoWmVicmFmaXNoU2FtcGxlcykgCiMgc2FtcGxlcwpzYW1wbGVzIDwtIGFzLmNoYXJhY3RlcihaZWJyYWZpc2hTYW1wbGVzJHNhbXBsZSkgIyB0byBzZWUgYWxsIHRoZSBzYW1wbGVzCnNhbXBsZXMKYGBgCkFzIHNhaWQgZWFybGllciwgd2Ugd2lsbCBwaWNrIGp1c3QgdHdvIGRldmVsb3BtZW50YWwgc3RhZ2VzOiAiemZfNTEyY2VsbHMiIGFuZCAiemZfcHJpbTYiLiBJbiB0aGUgdmlnbmV0dGUgKGFuZCBtYW51YWwpIHRoZSB3YXkgb2YgbG9hZGluZyBpbiB0aGVzZSBkYXRhIGlzIGJ5IHRoZSBmdW5jdGlvbiBfaW1wb3J0UHVibGljRGF0YSgpXy4gCldlIG5lZWQgdG8gc3BlY2lmeSB0aGUgc291cmNlLCBkYXRhc2V0LCBncm91cCwgYW5kIHNhbXBsZXMuIFRoZSBzb3VyY2UgaXMgIlplYnJhZmlzaERldmVsb3BtZW50IiBhbmQgdGhlIHJlc3QgYXJlIGZvdW5kIGluIHRoZSBkYXRhZnJhbWUgb2YgdGhlIF9aZWJyYWZpc2hTYW1wbGVzXzogWmVicmFmaXNoQ0FHRSwgZGV2ZWxvcG1lbnQsIHNhbXBsZW5hbWVzLgpUaGUgYXZvaWQgc3BlbGxpbmcgZXZlcnl0aGluZyBvdXQgd2UnbGwgdXNlIHRoZSB2ZWN0b3Igb2Ygc2FtcGxlczoKCmBgYHtyfQojIGNyZWF0aW5nIGEgQ0FHRXNldCBvYmplY3Q6Cm15Q0FHRXNldCA8LSBpbXBvcnRQdWJsaWNEYXRhKHNvdXJjZSA9ICJaZWJyYWZpc2hEZXZlbG9wbWVudCIsIGRhdGFzZXQgPSAiWmVicmFmaXNoQ0FHRSIsIApncm91cCA9ICJkZXZlbG9wbWVudCIsIHNhbXBsZSA9IHNhbXBsZXNbYyg0LDExKV0gICkKIyB3aGF0IGRvZXMgaXQgbG9vayBsaWtlPwpteUNBR0VzZXQKYGBgCgpIZXJlLCBpdCBpcyBnb29kIHRvIGRvdWJsZSBjaGVjayB0aGUgcmVmZXJlbmNlIGdlbm9tZSBhbmQgdGhlIHNhbXBsZSBsYWJlbHMuIEFsc28gdGhpcyBnaXZlcyB1cyBhbGwgdGhlIHNsb3RzIG9mIHRoZSBTNCBvYmplY3QuIFNvIGF0IHRoaXMgc3RhZ2Ugd2UgaGF2ZSBDVFNTIGluZm9ybWF0aW9uIGFzIHRoaXMgaXMgYWxyZWFkeSB0aGUgZm9ybSBvZiB0aGUgZGF0YSBvZiB0aGUgcGFja2FnZS4gT25seSB0aGUgdGFnIGNvdW50IGZvciBlYWNoIFRTUyBmb3IgYm90aCBzYW1wbGVzLCBhcyB0aGUgc2xvdCBvZiBOb3JtYWxpemVkIHRwbSBpcyBlbXB0eS4gQXMgd2VsbCBhcyB0aGUgcmVzdCBvZiB0aGUgb2JqZWN0LiAKCiMjIEZ1bmN0aW9ucyBvZiBDQUdFcgpXaXRoIHRoZSBmb2xsb3dpbmcgZmV3IHN0YW5kYXJkIENBR0VyIGZ1bmN0aW9ucyB3ZSB3aWxsICJmaWxsIGluIiB0aGVzZSBzbG90cy4gQW5kIHdoZW4gZnVsbCwgc2hvdyB5b3UgaG93IHdlIGNhbiByZXRyaWV2ZSBhbmQgZXhwb3J0IG9mIHRoZXNlIGRhdGEuCgojIyMgQ29ycmVsYXRpb24gcGxvdHMKRmlyc3QsIHdlIHdpbGwgbG9vayBhdCB0aGUgZGF0YSBnbG9iYWxseSBiZXR3ZWVuIHRoZSBzYW1wbGVzLiBDQUdFciBoYXMgYSBmdW5jdGlvbiBkbyB0aGlzIGZvciB5b3UgKHNob3duIGJlbG93KSB0byBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBzYW1wbGVzIGFuZCBwbG90cyB0aGUgcG5nIGluIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4gV2UgZmlsbCBmaXJzdCBzb3VyY2UgYW4gYWRqdXN0ZWQgdmVyc2lvbiBvZiB0aGlzIGZ1bmN0aW9uIHRvIGNvbnRyb2wgdGhlIG91dHB1dCBieSB1c2luZyBzb3VyY2UgKENyZWF0aW5nIGEgbmV3IGdlbmVyaWMgZnVuY3Rpb24gZm9yICdwbG90Q29ycmVsYXRpb24nKS4gCgoKCmBgYHtyfQpjb3JyLm0gPC0gcGxvdENvcnJlbGF0aW9uKG15Q0FHRXNldCwgc2FtcGxlcyA9ICJhbGwiLCBtZXRob2QgPSAicGVhcnNvbiIpIApjb3JyLm0gIyBjb3JyZWxhdGlvbgpgYGAKIVtjb3JyZWxhdGlvbl0oLi4vaW1hZ2VzL0NUU1NfcmF3X3ZhbHVlc19wYWlyd2lzZV9jb3JyZWxhdGlvbi5wbmcpCgojIyMgTm9ybWFsaXphdGlvbgpOZXh0LCB3ZSBuZWVkIHRvIG5vcm1hbGl6ZSBvdXIgZGF0YSB0byBhZGp1c3QgZm9yIGV4YW1wbGUgZGlmZmVyZW50IGxpYnJhcnkgc2l6ZXMgdG8gbWFrZSB0aGVtIGNvbXBhcmFibGUuIEhlcmUsIHdlJ2xsIHVzZSB0aGUgcG93ZXItbGF3IGJhc2VkIG5vcm1hbGl6YXRpb24gKHNlZSBAYmFsd2llcnopLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KIyBvdmVydmlldyBsaWJyYXJ5IHNpemVzOgpsaWJyYXJ5U2l6ZXMobXlDQUdFc2V0KQoKIyBub3JtYWxpc2F0aW9uIGZpcnN0IHBsb3QgdG8gYXNzZXNzIGFscGhhCnBsb3RSZXZlcnNlQ3VtdWxhdGl2ZXMobXlDQUdFc2V0LCBmaXRJblJhbmdlID0gYyg1LCAxMDAwKSwgb25lUGxvdCA9IFRSVUUpICMgcGxvdAojIGluc2VydCBhbHBoYSBpbiBub3JtYWxpemUgZnVuY3Rpb246Cm5vcm1hbGl6ZVRhZ0NvdW50KG15Q0FHRXNldCwgbWV0aG9kID0gInBvd2VyTGF3IixmaXRJblJhbmdlID0gYyg1LCAxMDAwKSwgYWxwaGEgPSAxLjIwLCBUID0gMSoxMF42KQpgYGAKCk5vdyB0aGUgQ1RTUyBpbmZvcm1hdGlvbiB0aGUgbm9ybWFsaXplZCB0cG0gaXMgYWRkZWQ6CmBgYHtyLCBldmFsID0gRkFMU0V9Cm15Q0FHRXNldApgYGAKCiMjIyBUYWcgY2x1c3RlcnMgb2YgQ1RTU3MKTGV0J3MgY3JlYXRlIHRhZyBjbHVzdGVycyBvZiBDVFNTcyB3aXRoIHRoZSBjbHVzdGVyQ1RTUyBmdW5jdGlvbiBvZiBDQUdFci4gQ1RTUyBpbiBjbG9zZSBwcm94aW1pdHkgb2YgZWFjaCBvdGhlciBnaXZlIHJpc2UgdG8gZnVuY3Rpb25hbGx5IHNpbWlhbHIgc2V0IG9mIHRyYW5zY3JpcHRzIHdpdGhpbiB0aGUgc2FtZSBwcm9tb3RlciBlbGVtZW50cy4gVGFnIGNsdXN0ZXJzIChUQ3MpIGFyZSBiYXNpY2FsbHkgbGFyZ2VyIHRyYW5zY3JpcHRpb25hbCB1bml0cyB0aGF0IGNvcnJlc3BvbmQgdG8gaW5kaXZpZHVhbCBwcm9tb3RlcnMuIAo8YnI+CkhlcmUsIHdlIHdpbGwgdXNlIGEgc2ltcGxlIGRpc3RhbmNlIG1lYXN1cmVtZW50IHRoYXQgaW5kaXZpZHVhbCBDVFNTIGNhbiBub3QgYmUgbW9yZSB0aGFuIDIwIGJwIGFwYXJ0LiBXZSBhbHNvIHNldCB0aGUgdGhyZXNob2xkIGF0IDEgd2hpY2ggbWVhbnMgdGhhdCBlYWNoIFRTUyBzaG91bGQgaGF2ZSAyIG9yIG1vcmUgdGFnIGNvdW50cyBpbiBhbGwgdGhlIHNhbXBsZXMgcHJpb3IgdG8gY2x1c3RlcmluZy4gQWRkaXRpb25hbGx5LCB0byByZW1vdmUgbG93IGZpZGVsaXR5IFRTU3M6IG5vIFRDIGFyZSBjYWxsZWQgd2l0aCBhIHNpbmdsZSBUQyBpZiB0aGUgbm9ybWFsaXplZCBzaWduYWwgaXMgYmVsb3cgNS4KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KIyBDbHVzdGVyaW5nIG9mIENUU1MgOikKY2x1c3RlckNUU1Mob2JqZWN0ID0gbXlDQUdFc2V0LCB0aHJlc2hvbGQgPSAxLCB0aHJlc2hvbGRJc1RwbSA9IFRSVUUsIAogICAgICAgICAgICBuclBhc3NUaHJlc2hvbGQgPSAxLCBtZXRob2QgPSAiZGlzdGNsdSIsIG1heERpc3QgPSAyMCwgCiAgICAgICAgICAgIHJlbW92ZVNpbmdsZXRvbnMgPSBUUlVFLCBrZWVwU2luZ2xldG9uc0Fib3ZlID0gNSkKYGBgCj4gS2VlcCBpbiBtaW5kIHRoYXQgdGhlc2UgYXJlIGRldGVybWluZWQgcGVyIHNhbXBsZQoKSGF2ZSBhIGxvb2sgYWdhaW4gYXQgdGhlIG15Q0FHRXNldCEgVGhlIFRhZyBjbHVzdGVyIGluZm9ybWF0aW9uIHNsb3QgaXMgZmlsbGVkIGluLgoKIyMjIFByb21vdGVyIHdpZHRoCkFub3RoZXIgZmVhdHVyZSB3ZSBjYW4gY2hlY2sgaXMgdGhlIHByb21vdGVyIHdpZHRoLiBUaGF0IGlzLCB0aGUgd2lkdGggb2YgZWFjaCB0YWcgY2x1c3RlciBwZXIgc2FtcGxlLiBDQUdFciBoYXMgYSBmdW5jdGlvbiB0aGF0IGNhbGN1bGF0ZXMgdGhlIFRDIHdpZHRoIGJ5IGNhY3VsYXRpbmcgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gb2YgdGFnIHNpZ25hbCBwZXIgVEMuIFN1YnNlcXVlbnRseSwgcHJvbW90ZXIgKFRDKSB3aWR0aCBpcyBkZWZpbmVkIGFzIHRoZSBkaXN0YW5jZSAoaW4gYnApIGJldHdlZW4gdHdvIHF1YW50aWxlcyBnZW5lcmFsbHkgc2V0IGF0IDAuMSBhbmQgMC45IHRvIGNhcHR1cmUgODAlIG9mIHRhZ3MuCiFbY3VtdWxhdGl2ZSBwcm9tb3Rlcl0oLi4vaW1hZ2VzL1Byb21vdGVyV2lkdGgucG5nKQoKYGBge3J9CiMgY2FsY3VsYXRlIGN1bWxhdGl2ZSBDVFNTIGRpc3RyaWJ1dGlvbjoKY3VtdWxhdGl2ZUNUU1NkaXN0cmlidXRpb24obXlDQUdFc2V0LCBjbHVzdGVycyA9ICJ0YWdDbHVzdGVycyIpCiMgZGV0ZXJtaW5lIHF1YW50aWxlIHBvc2l0aW9uczoKcXVhbnRpbGVQb3NpdGlvbnMobXlDQUdFc2V0LCBjbHVzdGVycyA9ICJ0YWdDbHVzdGVycyIsIHFMb3cgPSAwLjEsIHFVcCA9IDAuOSkKYGBgCldlIGNhbiB0aGVuIHBsb3QgYSBoaXN0b2dyYW0gb2YgdGhlIGludGVycXVhbnRpbGUgd2lkdGhzIHBlciBzYW1wbGUgd2l0aCBDQUdFciB0b28uCmBgYHtyfQojIHdlIGNhbiBwbG90IHRoZSBpbnRlcnF1YW50aWxlIHdpZHRoIHdpdGggQ0FHRXIgdG9vCnBsb3RJbnRlcnF1YW50aWxlV2lkdGgobXlDQUdFc2V0LCBjbHVzdGVycyA9ICJ0YWdDbHVzdGVycyIsdHBtVGhyZXNob2xkID0gMywgcUxvdyA9IDAuMSwgcVVwID0gMC45KQpgYGAKSWYgeW91IGxvb2sgYXQgdGhlc2UgdHdvIHBsb3RzLCB5b3UgY2FuIHNlZSB0aGF0IHRoZSA1MTIgY2VsbCBzdGFnZSBtb3JlIG5hcnJvd2VyIFRDcyB0aGFuIHByaW02IHdoaWNoIGlzIHdoYXQgd2Ugd291bGQgZXhwZWN0LiAKCiMjIyBDb25zZW5zdXMgQ2x1c3RlcnMKQ0FHRXIgdmlnbmV0dGU6IDxicj4KX1RhZyBjbHVzdGVycyBhcmUgb2Z0ZW4gc2FtcGxlLXNwZWNpZmljLCB0aHVzIGNhbiBiZSBwcmVzZW50IGluIG9uZSBzYW1wbGUgYnV0IGFic2VudCBpbiBhbm90aGVyLiBJbiBhZGRpdGlvbiwgaW4gbWFueSBjYXNlcyB0YWcgY2x1c3RlcnMgZG8gbm90IGNvaW5jaWRlIHBlcmZlY3RseSB3aXRoaW4gdGhlIHNhbWUgcHJvbW90ZXIgcmVnaW9uLCBvciB0aGVyZSBtaWdodCBiZSB0d28gY2x1c3RlcnMgaW4gb25lIHNhbXBsZSBhbmQgb25seSBvbmUgbGFyZ2VyIGluIHRoZSBvdGhlci4gVG8gYmUgYWJsZSB0byBjb21wYXJlIGdlbm9tZS13aWRlIHRyYW5zY3JpcHRpb25hbCBhY3Rpdml0eSBhY3Jvc3Mgc2FtcGxlcyBhbmQgdG8gcGVyZm9ybSBleHByZXNzaW9uIHByb2ZpbGluZywgYSBzaW5nbGUgc2V0IG9mIGNvbnNlbnN1cyBjbHVzdGVycyBuZWVkcyB0byBiZSBjcmVhdGVkLiBUaGlzIGlzIGRvbmUgdXNpbmcgYWdncmVnYXRlLVRhZ0NsdXN0ZXJzIGZ1bmN0aW9uLCB3aGljaCBhZ2dyZWdhdGVzIHRhZyBjbHVzdGVycyBmcm9tIGFsbCBzYW1wbGVzIGludG8gYSBzaW5nbGUgc2V0IG9mIG5vbi1vdmVybGFwcGluZyBjb25zZW5zdXMgY2x1c3RlcnM6XwoKYGBge3J9CmFnZ3JlZ2F0ZVRhZ0NsdXN0ZXJzKG15Q0FHRXNldCwgdHBtVGhyZXNob2xkID0gNSwgcUxvdyA9IDAuMSwgcVVwID0gMC45LCBtYXhEaXN0ID0gMTAwKQpgYGAKCgojIyMgRXhwcmVzc2lvbiAtIFNlbGYgb3JnYW5pc2luZyBtYXBzCkFzIENBR0Ugc2lnbmFscyBpcyBlc3NlbnRpYWxseSB0cmFuc2NyaXB0aW9uLCBsZXZlbHMgY2FuIGJlIGNvbXBhcmVkIGJldHdlZW4gZ3JvdXBzLiBUaGlzIGNhbiBiZSBkb25lIHBlciBDVFNTLCBUQywgb3IgY29uc2Vuc3VzIGNsdXN0ZXJzLiBDQUdFciBvZmZlcnMgdHdvIG1ldGhvZHMgdG8gY2x1c3RlciBnZW5lIGV4cHJlc3Npb24sIGstbWVhbnMgYW5kIHNlbGYtb3JnYW5pc2luZyBtYXBzIChTT00pLiBCb3RoIHJlcXVpcmUgdGhlIG51bWJlciBvZiBleHByZXNzaW9uIGNsdXN0ZXIgdG8gYmUga25vd24gaW4gX2EgcHJpb3JpXy4gPGJyPgpIZXJlIHdlIHdpbGwgcGVyZm9ybSB0aGUgU09NIGFsZ29yaXRobSBhdCBjb25zZW5zdXMgY2x1c3RlciBsZXZlbC4gV2Ugc2V0IHRoZSB0aHJlc2hvbGQgdG8gYXQgbGVhc3QgMTAgdHBtIChub3JtYWxpemVkKSBpbiBhdCBsZWFzdCBvbmUgc2FtcGxlLgoKYGBge3J9CmdldEV4cHJlc3Npb25Qcm9maWxlcyhteUNBR0VzZXQsIHdoYXQgPSAiY29uc2Vuc3VzQ2x1c3RlcnMiLCB0cG1UaHJlc2hvbGQgPSAxMCwKICAgICAgICBuclBhc3NUaHJlc2hvbGQgPSAxLCBtZXRob2QgPSAic29tIiwgeERpbSA9IDMsIHlEaW0gPSAyKQoKIyBpbiBhIG5pY2UgcGxvdDoKcGxvdEV4cHJlc3Npb25Qcm9maWxlcyhteUNBR0VzZXQsIHdoYXQgPSAiY29uc2Vuc3VzQ2x1c3RlcnMiKQpgYGAKCiMjIyBQcm9tb3RlciBzaGlmdGluZwpQcm9tb3RlciBzaGlmdGluZyBpcyBhIG1ldGhvZCBkZXNjcmliZWQgYnkgSGFiZXJsZSBfZXQgYWxfIGluIDIwMTQuIFRoZXkndmUgc2hvd24gdGhhdCB0aGUgc2FtZSBwcm9tb3RlciBjYW4gYmUgdXNlZCBkaWZmZXJlbnRseSBpbiBkaWZmZXJlbnQgc2FtcGxlcy4gVGhpcyBtZXRob2QgaGFzIGJlZW4gaW1wbGVtZW50ZWQgaW4gQ0FHRXIuIFNoaWZ0aW5nIGNhbiBiZSBkZXRlY3RlZCBiZXR3ZWVuIHR3byBpbmRpdmlkdWFsIHNhbXBsZXMgKG9yIGJldHdlZW4gdHdvIGdyb3VwcyBvZiBzYW1wbGVzIHRoYXQgYXJlIG1lcmdlZCBwZXIgZ3JvdXApCjxicj4KPGJyPgpfRm9yIGFsbCBwcm9tb3RlcnMgYSBzaGlmdGluZyBzY29yZSBpcyBjYWxjdWxhdGVkIGJhc2VkIG9uIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBvZiBDQUdFIHNpZ25hbCBhbG9uZyB0aGF0IHByb21vdGVyIGluIHRoZSB0d28gc2FtcGxlcy4gSW4gYWRkaXRpb24sIGEgbW9yZSBnZW5lcmFsIGFzc2Vzc21lbnQgb2YgZGlmZmVyZW50aWFsIFRTUyB1c2FnZSBpcyBvYnRhaW5lZCBieSBwZXJmb3JtaW5nIEtvbG1vZ29yb3YtU21pcm5vdiB0ZXN0IG9uIHRoZSBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbnMgb2YgQ0FHRSBzaWduYWwsIGFzIGRlc2NyaWJlZCBiZWxvdy4gVGh1cywgcHJpb3IgdG8gc2hpZnRpbmcgc2NvcmUgY2FsY3VsYXRpb24gYW5kIHN0YXRpc3RpY2FsIHRlc3RpbmcsIHdlIGhhdmUgdG8gY2FsY3VsYXRlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGFsb25nIGFsbCBjb25zZW5zdXMgY2x1c3RlcnM6XwoKPGltZyBzcmM9Ii4uL2ltYWdlcy9zaGlmdGluZ1Njb3JlLnBuZyIgd2lkdGg9IjQ1MCI+CgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KY3VtdWxhdGl2ZUNUU1NkaXN0cmlidXRpb24obXlDQUdFc2V0LCBjbHVzdGVycyA9ICJjb25zZW5zdXNDbHVzdGVycyIpCmBgYAoKRGV0ZXJtaW5lIHRoZSBzaGlmdGluZyBzY29yZToKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0Kc2NvcmVTaGlmdChteUNBR0VzZXQsIGdyb3VwWCA9IHNhbXBsZXNbNF0sIGdyb3VwWSA9IHNhbXBsZXNbMTFdLCB0ZXN0S1MgPSBUUlVFLCB1c2VUcG1LUyA9IEZBTFNFKQpgYGAKCl9WYWx1ZXMgb2Ygc2hpZnRpbmcgc2NvcmUgYXJlIGluIHJhbmdlIGJldHdlZW4gLUluZiBhbmQgMS4gUG9zaXRpdmUgdmFsdWVzIGNhbiBiZSBpbnRlcnByZXRlZCBhcyB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0aW9uIGluaXRpYXRpb24gaW4gdGhlIHNhbXBsZSB3aXRoIGxvd2VyIGV4cHJlc3Npb24gdGhhdCBpcyBoYXBwZW5pbmcgIm91dHNpZGUiIChlaXRoZXIgdXBzdHJlYW0gb3IgZG93bnN0cmVhbSkgb2YgdGhlIHJlZ2lvbiB1c2VkIGZvciB0cmFuc2NyaXB0aW9uIGluaXRpYXRpb24gaW4gdGhlIG90aGVyIHNhbXBsZS4gSW4gY29udHJhc3QsIG5lZ2F0aXZlIHZhbHVlcyBpbmRpY2F0ZSBubyBwaHlzaWNhbCBzZXBhcmF0aW9uLCBpLmUuIHRoZSByZWdpb24gdXNlZCBmb3IgdHJhbnNjcmlwdGlvbiBpbml0aWF0aW9uIGluIHRoZSBzYW1wbGUgd2l0aCBsb3dlciBleHByZXNzaW9uIGlzIGNvbXBsZXRlbHkgY29udGFpbmVkIHdpdGhpbiB0aGUgcmVnaW9uIHVzZWQgZm9yIHRyYW5zY3JpcHRpb24gaW5pdGlhdGlvbiBpbiB0aGUgb3RoZXIgc2FtcGxlLl8gPGJyPiA8YnI+Cl9UbyBhc3Nlc3MgYW55IGdlbmVyYWwgY2hhbmdlIGluIHRoZSBUU1MgdXNhZ2Ugd2l0aGluIHRoZSBwcm9tb3RlciByZWdpb24sIGEgdHdvc2FtcGxlCktvbG1vZ29yb3YtU21pcm5vdiAoSy1TKSB0ZXN0IG9uIGN1bXVsYXRpdmUgc3VtcyBvZiBDQUdFIHNpZ25hbCBhbG9uZyB0aGUKY29uc2Vuc3VzIGNsdXN0ZXIgaXMgcGVyZm9ybWVkLiBLLVMgdGVzdCBpcyBwZXJmb3JtZWQgdG8gYXNzZXNzIHdoZXRoZXIgdGhlIHR3byB1bmRlcmx5aW5nIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMgZGlmZmVyLiBUbyBvYnRhaW4gYSBQLXZhbHVlLCBzYW1wbGUgc2l6ZXMKdGhhdCBnZW5lcmF0ZWQgdGhlIEVDREZzIGFyZSByZXF1aXJlZCwgaW4gYWRkaXRpb24gdG8gYWN0dWFsIEstUyBzdGF0aXN0aWNzIGNhbGN1bGF0ZWQKZnJvbSBFQ0RGcy4gVGhlc2UgYXJlIGRlcml2ZWQgZWl0aGVyIGZyb20gcmF3IHRhZyBjb3VudHMgKHdoZW4gdXNlVHBtS1MgPSBGQUxTRSksIG9yCmZyb20gbm9ybWFsaXplZCB0cG0gdmFsdWVzICh3aGVuIHVzZVRwbUtTID0gVFJVRSkuIFAtdmFsdWVzIG9idGFpbmVkIGZyb20gSy1TIHRlc3RzCmFyZSBmdXJ0aGVyIGNvcnJlY3RlZCBmb3IgbXVsdGlwbGUgdGVzdGluZyB1c2luZyBCZW5qYW1pbmkgYW5kIEhvY2hlbmJlcmcgKEJIKSBtZXRob2QKYW5kIGZvciBlYWNoIFAtdmFsdWUgYSBjb3JyZXNwb25kaW5nIGZhbHNlLWRpc2NvdmVyeSByYXRlIChGRFIpIGlzIGFsc28gcmVwb3J0ZWQuXwo8YnI+Cjxicj4KSGVyZSB3ZSB3aWxsIHNldCB0aGUgdGhyZXNob2xkIG9mIHNoaWZ0aW5nIHNjb3JlIGF0IDAuNiBsaWtlIHRoZSBvcmlnaW5hbCBwYXBlciBhcyB3ZWxsIGFzIHNldHRpbmcgdGhlIEZEUiBhdCAwLjAxOgpgYGB7cn0Kc2hpZnRpbmcucHJvbW90ZXJzIDwtIGdldFNoaWZ0aW5nUHJvbW90ZXJzKG15Q0FHRXNldCwgdHBtVGhyZXNob2xkID0gNSwgc2NvcmVUaHJlc2hvbGQgPSAwLjYsIGZkclRocmVzaG9sZCA9IDAuMDEpCmhlYWQoc2hpZnRpbmcucHJvbW90ZXJzKQpgYGAKVGhpcyByZXR1cm5zIGdlbm9taWMgY29vcmRpbmF0ZXMgKG9mIGNvbmVuc3VzY2x1c3RlcnMpLCBzaGlmdGluZyBzY29yZSwgYW5kClAtdmFsdWUgKEZEUikgb2YgdGhlIHByb21vdGVycywgYXMgd2VsbCBhcyB0aGUgdmFsdWUgb2YgQ0FHRSBzaWduYWwgYW5kIHBvc2l0aW9uCm9mIHRoZSBkb21pbmFudCBUU1MgaW4gdGhlIHR3byBjb21wYXJlZCAoZ3JvdXBzIG9mKSBzYW1wbGVzLgoKYGBge3J9CmNvbG5hbWVzKHNoaWZ0aW5nLnByb21vdGVycylbNzo4XSA8LSBwYXN0ZShzYW1wbGVzW2MoNCwxMSldLCAiLnBvcyIsIHNlcCA9ICIiKSAjIHRvIHJlbWVtYmVyIGxhdGVyIHdoaWNoIHdhcyBncm91cCB4IGFuZCBncm91cCB5CndyaXRlLnRhYmxlKHNoaWZ0aW5nLnByb21vdGVycywgIi4uL0RhdGEvaW50ZXJtZWRpYXRlL1NoaWZ0ZWRQcm9tb3RlcnNfc2MwNl9mZHIwMDFfMnNhbXBsZXMudHh0IiwgY29sLm5hbWVzID0gVFJVRSwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UsIHNlcCA9ICJcdCIpCmBgYAoKU2F2ZSBteUNBR0VzZXQgYXMgYW4gaW50ZXJtZWRpYXRlIGZpbGUgZm9yIGlmIHlvdSB3YW50IHRvIHJldmlzaXQgaXQgbGF0ZXIgb24KCmBgYHtyfQpzYXZlKG15Q0FHRXNldCwgZmlsZSA9Ii4uL0RhdGEvaW50ZXJtZWRpYXRlL0NBR0VvYmplY3RfdHdvU2FtcGxlc19Qb3dOb21fYWxsU2xvdHMuUkRhdGEiKQpgYGAKCiMgRXhwb3J0IGJyb3dzZXIgdHJhY2tzIGZyb20gQ0FHRXIKCiMgRXhwb3J0IHRhYmxlcyAKCiMgRGF0YSBoYW5kbGluIGluIFIKCiMjIEFubm90YXRpb24gb2YgdHJhbnNjcmlwdHMKV2hlcmUgZG8gdGhlIGNhZ2Ugc2lnbmFsIAoKIyBEaW51Y2xlb3RpZGUgcGxvdHMKCiMgRGlmZmVyZW50aWFsIEdlbmUgRXhwcmVzc2lvbgpDQUdFIGRhdGEgY2FuIGFsc28gYmUgdXNlZCB0byBhc3Nlc3MgZXhwcmVzc2lvbiBvZiB0aGUgY1RTU3MuIEhlcmUsIHdlIHdpbGwgdXNlIHRoZSBSLXBhY2thZ2UgREVTZXEyIFtAbG92ZV9tb2RlcmF0ZWRfMjAxNF0uIE9yaWdpbmF0ZWQgZm9yIFJOQS1zZXEgZGF0YSBidXQgY2FuIGFsc28gaGFuZGxlIHNpbWlsYXIgZGF0YSBmcm9tIG90aGVyIGFzc2F5YSB0eXBlcyAoc3VjaCBhcyBDQUdFIGRhdGEpLiBUaGUgdmlnbmV0dGUgYW5kIHJlZmVyZW5jZSBtYW51YWwgY2FuIGJlIGRpc3BsYXllZCBieSBydW5uaW5nIHRoZSBjb2RlIGJlbG93IG9yIGNhbiBiZSBmb3VuZCBoZXJlIFtERVNlcTJdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9ERVNlcTIuaHRtbCkuIAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KYnJvd3NlVmlnbmV0dGVzKCJERVNlcTIiKQpgYGAKCiMjIFN1bW1hcnkgYW5kIGdvYWxzIG9mIHRoaXMgcHJhY3RpY2FsCjxicj4KX0NBR0VyXwoKKiBQcmVwYXJlIHRoZSByaWdodCBkYXRhIGZvcm1hdCBmcm9tIENBR0UgZGF0YSBmb3IgREVTRVEyCiogRXhwb3J0IHRoZSBkYXRhIGZyb20gQ0FHRXIKCl9ERVNlcTJfCgoqIE5vcm1hbGlzZSB0aGUgZGF0YQoqIERpZmZlcmVudGlhbCBleHByZXNzaW9uCgpfRm9sbG93IHVwXwoKKiBHZW5lIGFubm90YXRpb24KKiBHZW5lIG9udG9sb2d5CgoKIyMgMSBFeHBvcnRpbmcgZGF0YSBmcm9tIGEgQ0FHRXNldCBvYmplY3QKIyMjIENyZWF0aW5nIGEgY291bnQgdGFibGUgaW5wdXQgZm9yIERFU2VxMgo8YnI+CldlIGhhdmUgd29ya2VkIHVudGlsIG5vdyB3aXRoIHRoZSB0d28gc2FtcGxlcy4gSG93ZXZlciwgZm9yIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgeW91IHdpbGwgbmVlZCBtb3JlIHNhbXBsZXMgKHJlcGxpY2F0ZXMgYW5kL29yIG1vcmUgb2YgdGhlIHNhbWUgY29uZGl0aW9uKS4gVG8gdGhpcyBlbmQsIHdlJ2xsIGluY2x1ZGUgdHdvIG1vcmUgc2FtcGxlcyB0byB0aGUgbWl4IHRvIGZvbGxvdyB0aGUgbW9yZSBzdGFuZGFyZCB3b3JrLWZsb3cgYW5kIGdlbmVyYXRlICpwKi12YWx1ZXMuIFRoZSB0d28gYWRkaXRpb25hbCBzYW1wbGVzIGFyZSBhZ2FpbiBmcm9tIHRoZSBzYW1lIFIgcGFja2FnZSAoWmVicmFmaXNoRGV2ZWxvcG1lbnRhbENBR0UpLiBUaGUgYW5hbHlzaXMgd2lsbCBiZSBvbiBlYXJseSBzdGFnZSBleHByZXNzaW9uIHZzIGxhdGVyIHN0YWdlIGV4cHJlc3Npb246IHpmXzY0Y2VsbHMgYW5kIHpmXzUxMmNlbGxzIHZzIHpmX3ByaW02IGFuZCB6Zl9wcmltMjAuIAoKW0RFU2VxMl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0RFU2VxMi5odG1sKSBhY2NlcHRzIG1hdHJpY2VzIG9mIHJlYWQgY291bnRzIGFzIGlucHV0IGFuZCB0aGlzIGlzIGV4YWN0bHkgd2hhdCB3ZSB3aWxsIGV4cG9ydCBmcm9tIENBR0VyLiBJdCBleHBlY3RzIGNvdW50IGRhdGEgaW4gdGhlIGZvcm0gb2YgYSBtYXRyaXggb2YgaW50ZWdlciB2YWx1ZXMuIFRoZSB2YWx1ZSBpbiB0aGUgaS10aCByb3cgYW5kIHRoZSBqLXRoIGNvbHVtbiBvZiB0aGUgbWF0cml4IHRlbGxzIGhvdyBtYW55IHJlYWRzIGNhbiBiZSBhc3NpZ25lZCB0byBlYWNoIGNUU1Mgc2l0ZSBpIGluIHNhbXBsZSBqLiBUaGUgdmFsdWVzIGluIHRoZSBtYXRyaXggc2hvdWxkIGJlIHVuLW5vcm1hbGl6ZWQgY291bnRzIG9mIHNlcXVlbmNpbmcgcmVhZHMuCgpDb25zZW5zdXMgc2l0ZSAocm93KSB8IFNhbXBsZSAxIHRwbSB8IFNhbXBsZSAyIHRwbSB8IFNhbXBsZSAzIHRwbSB8IFNhbXBsZSA0IHRwbSAgCi0tLS0tLS0tLS0tLS0gIHwgLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLQoxICAgICAgICAgICAgICB8IDAgICAgfCAxIHwgNDQgfCA2MCAgICAgICAgCjIgICAgICAgICAgICAgIHwgNCB8IDEwIHwgNiB8IDkgICAgICAgCgo+IDxzbWFsbD4hISAiVGhlIERFU2VxMiBtb2RlbCBpbnRlcm5hbGx5IGNvcnJlY3RzIGZvciBsaWJyYXJ5IHNpemUsIHNvIHRyYW5zZm9ybWVkIG9yIG5vcm1hbGl6ZWQgdmFsdWVzIHN1Y2ggYXMgY291bnRzIHNjYWxlZCBieSBsaWJyYXJ5IHNpemUgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIGlucHV0IiA8L3NtYWxsPgoKVGhlIGNvZGUgYmVsb3cgd2FzIHJ1biB0byBwcm9kdWNlIHRoZSBjb25zZW5zdXMgY2x1c3RlcnMgZm9yIHRoZSBmb3VyIHNhbXBsZXMgYXMgdGhpcyB3b3VsZCB0YWtlIHRvIGxvbmcgdG8gZG8gb24gdGhlIGRheS4gVGhlIG9uZSBpbXBvcnRhbnQgKGRpZmZlcmVudCBzdGVwKSBpcyBzaG93biBiZWxvdyBpbiB0aGUgY29kZS4gRG9uJ3QgcnVuIHRoZSBjb2RlICh0b2RheSkuCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBwYWNrYWdlcwpyZXF1aXJlKFplYnJhZmlzaERldmVsb3BtZW50YWxDQUdFKQpyZXF1aXJlKENBR0VyKQojIGxvYWQgZGF0YQpkYXRhKFplYnJhZmlzaFNhbXBsZXMpCmFzLmNoYXJhY3RlcihaZWJyYWZpc2hTYW1wbGVzJHNhbXBsZSkKbXlDQUdFc2V0IDwtIGltcG9ydFB1YmxpY0RhdGEoc291cmNlID0gIlplYnJhZmlzaERldmVsb3BtZW50IiwgZGF0YXNldCA9ICJaZWJyYWZpc2hDQUdFIiwgCmdyb3VwID0gImRldmVsb3BtZW50Iiwgc2FtcGxlID0gYXMuY2hhcmFjdGVyKFplYnJhZmlzaFNhbXBsZXMkc2FtcGxlW2MoMzo0LDExOjEyKV0gKSApCgojIENUU1MgdGFnIGNvdW50CmN0c3MgPC0gQ1RTU3RhZ0NvdW50KG15Q0FHRXNldCkKCiMKIyBUbyBrZWVwIHVzaW5nIHRoZSByYXcgY291bnRzIGluIGFsbCBkb3duc3RyZWFtIHN0ZXBzLCB0aGUgbm9ybWFsaXplVGFnQ291bnQgZnVuY3Rpb24gb2YgQ0FHRXIgc2hvdWxkIGJlIHVzZWQgd2l0aCB0aGUgbWV0aG9kIHNldCB0byAibm9uZSIuIE5vdGUgdGhhdCBub3JtYWxpemVUYWdDb3VudCBmdW5jdGlvbiBoYXMgdG8gYmUgYXBwbGllZCB0byBDQUdFc2V0IG9iamVjdCBiZWZvcmUgbW92aW5nIHRvIG5leHQgc3RlcHMuCm5vcm1hbGl6ZVRhZ0NvdW50KG15Q0FHRXNldCwgbWV0aG9kID0gIm5vbmUiKQojCgoKIyBDbHVzdGVyaW5nIG9mIENUU1M6IGxvdyBmaWRlbGl0eSBjVFNTcyBhcmUgcmVtb3ZlZCAoZWFjaCBjbHVzdGVyIHdpdGggb25seSBvbmUgY1RTUyBzaWduYWwgPCA1KS4KY2x1c3RlckNUU1Mob2JqZWN0ID0gbXlDQUdFc2V0LCB0aHJlc2hvbGQgPSAxLCB0aHJlc2hvbGRJc1RwbSA9IFRSVUUsIAogICAgICAgICAgICBuclBhc3NUaHJlc2hvbGQgPSAxLCBtZXRob2QgPSAiZGlzdGNsdSIsIG1heERpc3QgPSAyMCwgCiAgICAgICAgICAgIHJlbW92ZVNpbmdsZXRvbnMgPSBUUlVFLCBrZWVwU2luZ2xldG9uc0Fib3ZlID0gNSkKCiMgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gYW5kIHF1YW50aWxlIHBvc2l0aW9ucwpjdW11bGF0aXZlQ1RTU2Rpc3RyaWJ1dGlvbihteUNBR0VzZXQsIGNsdXN0ZXJzID0gInRhZ0NsdXN0ZXJzIikKcXVhbnRpbGVQb3NpdGlvbnMobXlDQUdFc2V0LCBjbHVzdGVycyA9ICJ0YWdDbHVzdGVycyIsIHFMb3cgPSAwLjEsIHFVcCA9IDAuOSkKCiMgYWdncmVnYXRlIHRoZSBjbHVzdGVycyBhY3Jvc3MgdGhlIHNhbXBsZXM6CmFnZ3JlZ2F0ZVRhZ0NsdXN0ZXJzKG15Q0FHRXNldCwgdHBtVGhyZXNob2xkID0gNSwgcUxvdyA9IDAuMSwgcVVwID0gMC45LCBtYXhEaXN0ID0gMTAwKQpzYXZlKG15Q0FHRXNldCwgZmlsZSA9ICIuLi9EYXRhL3Byb3ZpZGVkL0FnZ3JlZ2F0ZWRUYWdDbHVzXzAxMDlfNFNhbXBsZXMuUkRhdGEiKQpgYGAKCjxicj4KVG8gYmUgYWJsZSB0byBjb21wYXJlIHRyYW5zY3JpcHRpb25hbCBhY3Rpdml0eSBhY3Jvc3MgdGhlc2Ugc2FtcGxlcywgY29uc2Vuc3VzIGNsdXN0ZXJzIHdpbGwgYmUgdXNlZCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcy4gVGhpcyBpcyBwcm92aWRlZCB0byB5b3UgaW4gYSAuUkRhdGEgZmlsZS4gVGhlIGZvbGxvd2luZyBzdGVwcyBpbnZvbHZlIGNyZWF0aW5nIHRoZSBjb3VudCBkYXRhIHRhYmxlIGZvciBlYWNoIGNvbnNlbnN1cyBjbHVzdGVyLCBtZXJnZSBpdCB3aXRoIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgY29uc2Vuc3VzLCBhbmQgdG8gd3JpdGUgYSBmaWxlIHdpdGggdGhlIG91dHB1dCBpbiB0aGUgaW50ZXJtZWRpYXRlIGRpcmVjdG9yeSB3aXRoaW4gdGhlIERhdGEgZGlyZWN0b3J5LiAKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CiMgcGFja2FnZQpyZXF1aXJlKENBR0VyKQoKIyBsb2FkIHRoZSBkYXRhIHByb2R1Y2VkIGJ5IHRoZSBjb2RlIGFib3ZlCmxvYWQoIi4uL0RhdGEvcHJvdmlkZWQvQWdncmVnYXRlZFRhZ0NsdXNfMDEwOV80U2FtcGxlcy5SRGF0YSIpCgojIGNyZWF0ZSBjb3VudCB0cG0gbWF0cml4IHBlciBjb25zZW5zdXMgY2x1c3RlciBmb3IgZWFjaCBzYW1wbGUKY291bnQuZGYgPC0gZGF0YS5mcmFtZShjb25zZW5zdXNDbHVzdGVyc1RwbShteUNBR0VzZXQpKQoKIyBhbmQgdGhlIGNvbnNlbnN1cyBjb29yZGluYXRlcyAoc2FtZSBvcmRlcikKY29uc2Vuc3VzLmluZm8gPC0gY29uc2Vuc3VzQ2x1c3RlcnMobXlDQUdFc2V0KQoKIyBjcmVhdGUgaWRlbnRpZmllcnMgdG8gbGluayBiYWNrCmNvbnNlbnN1cy5pbmZvJGNvbnNfY2x1c19pZCA8LSBwYXN0ZSgiY2lkXyIsMTpucm93KGNvbnNlbnN1cy5pbmZvKSwgc2VwID0gIiIpCnJvd25hbWVzKGNvdW50LmRmKSA8LSBjb25zZW5zdXMuaW5mbyRjb25zX2NsdXNfaWQKCiMgc2F2ZSB0aGUgY29tYmluZWQgaW5mbyBhbmQgY291bnQgdHBtIGFzIGludGVybWVkaWF0ZSBmaWxlcyAKIyB0aGUgb3JkZXIgaXMgdGhlIHNhbWUgc28gd2UgY2FuIGVhc2lseSB1c2UgY2JpbmQKY291bnQuY29uc2Vuc3VzLmluZm8gPC0gY2JpbmQoY29uc2Vuc3VzLmluZm9bLC0xXSwgY291bnQuZGYpCndyaXRlLnRhYmxlKGNvdW50LmNvbnNlbnN1cy5pbmZvLCAiLi4vRGF0YS9pbnRlcm1lZGlhdGUvQ291bnRUYWJsZV9Db25zZW5zdXNfNFNhbXBsZXMudHh0IiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gRkFMU0UsIHNlcCA9ICJcdCIsIHF1b3RlID0gRkFMU0UpCgojIHJlbW92ZSB1bm5lY2Vzc2FyeSBmaWxlcyAKcm0obXlDQUdFc2V0LCBzYW1wbGVzLCBjb25zZW5zdXMuaW5mbykKYGBgCgo8YnI+CgojIyAyIERFU2VxMiBEYXRhIEFuYWx5c2lzCjxicj4KRmlyc3QsIHdlIHdpbGwgbWFrZSBhIERFU2VxRGF0YVNldCBvYmplY3QgZnJvbSB0aGUgY291bnQgdGFibGUgYW5kIGFkZCB0aGUgImZvcm11bGEiIHdoaWNoIGlzIHRoZSBkZXNpZ24gb2YgdGhlIGFuYWx5c2lzIGRvd25zdHJlYW0gKGEgbGluZWFyIG1vZGVsOiB+IGNvbmRpdGlvbikuIEluIHRoaXMgZXhhbXBsZSB3ZSBhcmUgb25seSB1c2luZyB0aGUgdmFyaWFibGUgKGVhcmx5IHZzIGxhdGUpLCBob3dldmVyLCBpZiB5b3Ugd2FudCB0byBhZGQgY292YXJpYXRlcyBpbiB0aGUgbW9kZWwgKGUuZy4gYmF0Y2gpIHRoZXNlIHdvdWxkIGJlIGV4dHJhIGNvbHVtbnMgaW4gaW5mby5kZiAoc2VlIGJlbG93KSBhbmQgZ2l2ZW4gaW4gdGhlIG1vZGVsIGxpa2UgOiB+IGNvbHVtbiBuYW1lIGNvdmFyaWF0ZSArIHZhcmlhYmxlLiBTbyB0aGUgdmFyaWFibGUgd2lsbCBiZSB0aGUgbGFzdCBpbiB0aGUgbW9kZWwuIAoKYGBge3J9CiMgREVTZXEyIGV4cGVjdHMgYSBtYXRyaXggb2YgY291bnQgdGFibGU6CmNvdW50Lm1hdHJpeCA8LSBhcy5tYXRyaXgoY291bnQuZGYpICMgaWYgZnJvbSBwcmV2aW91c2x5IHNhdmVkIGZpbGU6IGFzLm1hdHJpeChjb3VudC5jb25zZW5zdXMuaW5mb1ssNzoxMF0pCmhlYWQoY291bnQubWF0cml4KQpgYGAKYGBge3J9CiMgdGhlIGNvbmRpdGlvbiBmb3IgdGhlIGFuYWx5c2lzIGluIGRhdGEuZnJhbWU6CnNhbXBsZXMgPC0gY29sbmFtZXMoY291bnQubWF0cml4KQppbmZvLmRmIDwtIGRhdGEuZnJhbWUoY29uZGl0aW9uID0gZmFjdG9yKHggPSBjKCJlYXJseSIsICJlYXJseSIsICJsYXRlIiwgImxhdGUiKSwgbGV2ZWxzID0gYygiZWFybHkiLCJsYXRlIikpLCByb3cubmFtZXMgPSBzYW1wbGVzKQoKYGBgCgo8YnI+ClRoZSByZWFzb24gZm9yIHNwZWNpZnlpbmcgdGhlIGxldmVscyBvZiB0aGUgZmFjdG9yIGlzIGJlY2F1c2UgYnkgZGVmYXVsdCwgUiB3aWxsIGhhbmRsZSBmYWN0b3IgbGV2ZWxzIGJhc2VkIG9uIGFscGhhYmV0aWNhbCBvcmRlci4gSXQgaXMgZ29vZCBwcmFjdGljZSB0byBnZXQgaW50bywgZXNwZWNpYWxseSBpbiBhbmFseXNlcyB3aGVyZSBpdCBtYXR0ZXJzIHdoaWNoIGxldmVsIHlvdSB3YW50IHRvIGNvbXBhcmUgYWdhaW5zdC4gVGh1cyBpZGVudGlmeWluZyBjb3JyZWN0bHkgdXAtIG9yIGRvd24tcmVndWxhdGVkIGdlbmVzIGluIHRoaXMgY2FzZS4KPGJyPgpUaGUgZmlyc3QgdGhpbmcgdG8gZG8gbm93IGlzIHRvIGNyZWF0ZSBhIERFU2VxRGF0YVNldCBvYmplY3QgdGhhdCBzdG9yZXMgdGhlIGNvdW50IG1hdHJpeCBhbmQgZGVzaWduOgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KcmVxdWlyZShERVNlcTIpCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGNvdW50Lm1hdHJpeCwgY29sRGF0YSA9IGluZm8uZGYsIGRlc2lnbiA9IH4gY29uZGl0aW9uKQpgYGAKPGJyPgpMZXQncyBmaXJzdCBkbyBhIHNpbXBsZSBoZWF0bWFwIHRvIGNoZWNrIGhvdyBzaW1pbGFyIChvciBkaXNzaW1pbGFyKSB0aGUgZm91ciBzYW1wbGVzIGFyZSBvbiBhIGdlbm9tZS13aWRlIHRyYW5zY3JpcHRpb24gbGV2ZWwuIEZvciB0aGlzIHdlIHdpbGwgdXNlIHRoZSB0cmFuc2Zvcm1lZCBkYXRhIGFzIHBlcmZvcm1lZCBieSBERVNlcTIuIEl0IGNhbiBkbyB0aHJlZSBkaWZmZXJlbnQgdHlwZXMgb2YgdHJhbnNmb3JtYXRpb24gYW5kIGhlcmUgd2Ugd2lsbCB1c2UgdGhlIGZ1bmN0aW9uCl9fcmxvZ19fLCB3aGljaCBzdGFuZHMgZm9yIHJlZ3VsYXJpemVkIGxvZy4gSXQgdHJhbnNmb3JtcyB0aGUgb3JpZ2luYWwgY291bnQgZGF0YSB0byB0aGUgbG9nMiBzY2FsZSBieSBmaXR0aW5nIGEgbW9kZWwgd2l0aCBhIHRlcm0gZm9yIGVhY2ggc2FtcGxlIGFuZCBhIHByaW9yIGRpc3RyaWJ1dGlvbiBvbiB0aGUgY29lZmZpY2llbnRzIHdoaWNoIGlzIGVzdGltYXRlZCBmcm9tIHRoZSBkYXRhIChTZWUgREVTc2VxIHZpZ25ldHRlIGZvciBtb3JlIGluZm8pLiAKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CiMgcGFja2FnZQpyZXF1aXJlKFJDb2xvckJyZXdlcikKcmVxdWlyZShwaGVhdG1hcCkKIyBFeHRyYWN0aW5nIHRyYW5zZm9ybWVkIHZhbHVlczoKcmxkIDwtIHJsb2coZGRzLCBibGluZD1GQUxTRSkKIyBoZWF0bWFwCnNhbXBsZURpc3RzIDwtIGRpc3QodChhc3NheShybGQpKSkKc2FtcGxlRGlzdE1hdHJpeCA8LSBhcy5tYXRyaXgoc2FtcGxlRGlzdHMpCgpyb3duYW1lcyhzYW1wbGVEaXN0TWF0cml4KSA8LSBybGQkY29uZGl0aW9uCmNvbG5hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIE5VTEwKCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKCByZXYoYnJld2VyLnBhbCg5LCAiQmx1ZXMiKSkgKSgyNTUpCgpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3M9c2FtcGxlRGlzdHMsCiAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzPXNhbXBsZURpc3RzLGNvbD1jb2xvcnMpCmBgYAoKSW5kZWVkLCB0aGUgc2FtcGxlcyBhcmUgY2x1c3RlcmluZyBhY2NvcmRpbmcgdG8gdGhlIGRldmVsb3BtZW50YWwgdGltZSBmcmFtZS4gVGhlcmUgYXJlIG1vcmUgdGhpbmdzIHlvdSBjYW4gZG8gYW5kIGNoZWNrIHN1Y2ggYXMgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyB0aGF0IGFyZSBhbHNvIGRlc2NyaWJlZCBpbiB0aGUgdmlnbmV0dGUuCjxicj4KPGJyPgpUaGUgc3RhbmRhcmQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgc3RlcHMgYXJlIHdyYXBwZWQgaW50byBhIHNpbmdsZSBmdW5jdGlvbiwgYCBfX0RFU2VxX18uIFJlc3VsdHMgdGFibGVzIGFyZSBnZW5lcmF0ZWQgdXNpbmcgdGhlIGZ1bmN0aW9uIF9fcmVzdWx0c19fLCB3aGljaCBleHRyYWN0cyBhIHJlc3VsdHMgdGFibGUgd2l0aCBsb2cyIGZvbGQgY2hhbmdlcywgcHZhbHVlcyBhbmQgYWRqdXN0ZWQgcHZhbHVlcy4gVGhlIHRleHQsIGNvbmRpdGlvbiB0cmVhdGVkIHZzIHVudHJlYXRlZCwgdGVsbHMgeW91IHRoYXQgdGhlIGVzdGltYXRlcyBhcmUgb2YgdGhlIGxvZ2FyaXRobWljIGZvbGQgY2hhbmdlIGxvZzIgKHRyZWF0ZWQvdW50cmVhdGVkKS4gCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQojIHRoZSBhbmFseXNpcwpkZHMgPC0gREVTZXEoZGRzKQojIHJlc3VsdHMKcmVzIDwtIHJlc3VsdHMoZGRzKQpgYGAKCjxicj4gCk5leHQsIHdlIHdhbnQgdG8ga25vdyB3aGF0IGFyZSB0aGUgbG93ZXN0IGFkanVzdGVkIHB2YWx1ZXMgYXMgdGhlc2UgYXJlIG9mIGludGVyZXN0LCB3aGljaCBnZW5lcyB0aGV5IHJlcHJlc2VudCwgYW5kIGdlbmUgb250b2xvZ3kgZm9yIHBhdHRlcm5zIGluIHRoZSBkYXRhLgo8YnI+CkZpcnN0LCB3ZSBhcmUgYWRkaW5nIHRoZSBnZW5vbWljIGNvb3JkaW5hdGVzIG9mIHRoZSBjVFNTIHN0aWxsIHN0b3JlZCBpbjoKYGBge3IsIGV2YWw9RkFMU0V9CmNvdW50LmNvbnNlbnN1cy5pbmZvCmBgYCAKYW5kIHRoZW4gcmVvcmRlciB0aGUgcmVzdWx0cyBhY2NvcmRpbmcgdG8gbG93ZXN0IHAtdmFsdWU6CgpgYGB7cn0KcmVzLmluZm8gPC0gY2JpbmQoY291bnQuY29uc2Vuc3VzLmluZm9bLGMoMTo0LDYpXSwgZGF0YS5mcmFtZShyZXNAbGlzdERhdGEpKQpyZXMuaW5mbyA8LSByZXMuaW5mb1tvcmRlcihyZXMuaW5mbyRwYWRqKSxdCmhlYWQocmVzLmluZm8pCmBgYApBbiBlYXN5IHN1bW1hcnk6CmBgYHtyfQpzdW1tYXJ5KHJlcykgICMgb3JpZ25hbCBTNCBvdXRwdXQgb2YgREVTZXEKYGBgCgpUaGUgYW1vdW50IGNUU1MgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIChwYWRqIDwgMC4wNSk6CmBgYHtyfQpzdW0ocmVzJHBhZGogPCAwLjA1LCBuYS5ybT1UUlVFKQpgYGAKClNhdmUgdGhlIHJlc3VsdHMgaW4gYSB0YWJsZSBpbiB0aGUgSW50ZXJtZWRpYXRlIGRpcmVjdG9yeToKYGBge3J9CiMgRGF0YWZyYW1lIGZvciBkb3duc3RyZWFtCiMgc2F2ZSBhcyBpbnRlcm1lZGlhdGUgZmlsZQp3cml0ZS50YWJsZShyZXMuaW5mbywgIi4uL0RhdGEvaW50ZXJtZWRpYXRlL0RpZmZFeHByZXNzaW9uX0NvbnNlbnN1c180U2FtcGxlcy50eHQiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0IikKIyByZW1vdmUgcHJldmlvdXMgZmlsZXMKcm0ocmVzKQpgYGAKCjxicj4KCiMjIDMgR2VuZSBhbm5vdGF0aW9uIGFuZCBHZW5lIG9udG9sb2d5Cjxicj4KTGV0J3Mgc3RhcnQgYnkgaGF2aW5nIGEgdHhkYiBhZ2F1YiBmb3Igb3VyIHNhbXBsZXMuIFRoZSB0eGRiIHdhcyBjcmVhdGVkIHdpdGggdGhlIGNvZGUgYmVsb3cgYW5kIF9jYW4gYmUgZm91bmQgaW4gdGhlIHByb3ZpZGVkIGRhdGEgZGlyZWN0b3J5Xy4gCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQojIGRhbiByZXJpbyB2NwpyZXF1aXJlKEdlbm9taWNGZWF0dXJlcykKcmVxdWlyZShBbm5vdGF0aW9uRGJpKQp0eGRiIDwtIG1ha2VUeERiRnJvbVVDU0MoImRhblJlcjciLCAiZW5zR2VuZSIpCnNhdmVEYih0eGRiLCBmaWxlID0gIi4uL0RhdGEvcHJvdmlkZWQvdHhkYl9EYW5SZXI3LnNxbGl0ZSIpCmBgYAoKTG9hZCBpbiB0aGUgdGhlIHR4ZGI6CmBgYHtyfQpyZXF1aXJlKEdlbm9taWNGZWF0dXJlcykKcmVxdWlyZShBbm5vdGF0aW9uRGJpKQp0eGRiIDwtIGxvYWREYigiLi4vRGF0YS9wcm92aWRlZC90eGRiX0RhblJlcjcuc3FsaXRlIikKYGBgCgpMZXQncyBydW4gdGhyb3VnaCBhIGZldyB0aGluZ3MgZmlyc3QgYW5kIGJ1aWxkIGEgZnVuY3Rpb24gZm9yIGxhdGVyIHB1cnBvc2VzLiBGaXJzdCBkZWZpbmluZyBnZW5lIGZlYXR1cmVzIHRvIGxhdGVyIGFzc2VzcyBpbiBvdXIgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGNUU1NzOiBwcm9tb3RlcnMsIHVwc3RyZWFtIHNlcSAoNWtiIG9mIHByb21vdGVyKSwgZXhvbnMsIGludHJvbnMsIGFuZCBnZW5lIGFzIGFubm90YXRlZCBpbiBlbnNlbWJsZS4gVGhlc2Ugd2lsbCBiZSBpbiBHUmFuZ2VzIG9iamVjdHMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgUHJvbW90ZXJzICg1MDBicCB3aW5kb3cgYXJvdW5kIHJlZmdlbmUgVFNTKQpwcm9tb3RlcnMgPSB0cmltKHByb21vdGVycyh0eGRiLCB1cHN0cmVhbT01MDAsIGRvd25zdHJlYW09NTAwKSkKIyA1IGtiIHVwc3RyZWFtIC0gNTAwYnAgCnVwc3RyZWFtID0gdHJpbShmbGFuayhwcm9tb3RlcnMsIDUwMDApKQojIGV4b25zCmV4b25zIDwtIGV4b25zKHR4ZGIpIAojIGV4b25zIGdyb3VwZWQgYnkgZ2VuZToKZXhvbnNfZ2VuZSA8LSByZWR1Y2UoZXhvbnNCeSh0eGRiLCJnZW5lIikpCgojIGludHJvbnMKaW50cm9ucyA9IGludHJvbnNCeVRyYW5zY3JpcHQodHhkYikKIyBnZW5lcwpnZW5lID0gZ2VuZXModHhkYikKYGBgCgpUbyB1c2UgdGhlIHVzZWZ1bCBmZWF0dXJlcyBvZiBHZW5vbWljUmFuZ2VzIHdlIHdpbGwgaGF2ZSB0byBjb252ZXJ0IG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHQgdG8gYSBHUmFuZ2VzIG9iamVjdCB0b286CgpgYGB7cn0KIyBwYWNrYWdlcwpyZXF1aXJlKEJTZ2Vub21lLkRyZXJpby5VQ1NDLmRhblJlcjcpCgp0b3RhbCA9IHJlcy5pbmZvIApnciA8LSBHUmFuZ2VzKHNlcW5hbWVzID0gdG90YWwkY2hyLAogICAgICAgICAgICAgICAgcmFuZ2VzID0gSVJhbmdlcyhzdGFydCA9IHRvdGFsJHN0YXJ0LGVuZCA9IHRvdGFsJGVuZCksCiAgICAgICAgICAgICAgICBzdHJhbmQgPSB0b3RhbCRzdHJhbmQsCiAgICAgICAgICAgICAgICBjb25zX2NsdXNfaWQgPSB0b3RhbCRjbHVzLmdlbmUsICMgdGhlIGlkZW50aWZpZXIKICAgICAgICAgICAgICAgIHNlcWxlbmd0aHMgPSBzZXFsZW5ndGhzKERyZXJpbykpCmBgYAoKCgoKCgoKIyBSZWZlcmVuY2VzCg==